package com.lzz.utils;

import com.lzz.redis.FastJsonRedisSerializer;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;
import java.time.Duration;

/**
 * redis 配置类
 * @author zhenzhen
 * @date 2018-12-19 下午5:37
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    /**
     * 缓存管理器
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

        // 使用fastjson序列化
        FastJsonRedisSerializer fastJsonSerializer = new FastJsonRedisSerializer(Object.class);

        // entryTtl()设置缓存有效期一小时
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                // 设置key为string序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 设置value为fastjson序列化
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonSerializer))
                // 不缓存空值
                .disableCachingNullValues();

        // 使用自定义的缓存配置初始化一个cacheManager
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheConfiguration)
                .build();
        return redisCacheManager;
    }

    /**
     * 在使用@cacheable时，如果不指定key，则使用找个默认的key生成器生成的key
     * @return
     */
    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return new SimpleKeyGenerator(){
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(".").append(method.getName());

                StringBuilder paramsSb = new StringBuilder();
                for (Object param : params) {
                    if (param != null) {
                        paramsSb.append(param.toString());
                    }
                }
                if (paramsSb.length() > 0) {
                    sb.append("_").append(paramsSb);
                }
                return sb.toString();
            }
        };
    }

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();

        // 使用fastjson序列化
        FastJsonRedisSerializer fastJsonSerializer = new FastJsonRedisSerializer(Object.class);

        // value值的序列化采用fastJsonRedisSerializer
        template.setValueSerializer(fastJsonSerializer);
        template.setHashValueSerializer(fastJsonSerializer);

        // key的序列化采用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    /**
     * StringRedisTemplate是RedisTemplate的一个子类，也可以说是一个特例RedisTemplate<String, String>
     * 在这种都是string类型的特例下，key和value序列化和反序列化都用了StringRedisSerializer序列化工具，字符集用的是UTF-8
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory){
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    /**
     * 设置默认的序列化方式。本来默认的方式是JDK的，现在改成了自己的方式。
     * 这样用redis在保存session中的对象时，不会出现错误
     * JDK默认的序列化方式仅支持String类型
     * @return
     */
//    @Bean
//    RedisSerializer<Object> springSessionDefaultRedisSerializer() {
//        return new FastJsonRedisSerializer<Object>(Object.class);
//    }
}
